/*
 * Copyright (C) 2010 Graham Allan
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.strath.cis.grallan.findbugs.adaptiveranking.population;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;

import edu.umd.cs.findbugs.BugInstance;

/**
 * @author Graham Allan (grallan@cis.strath.ac.uk)
 */
public abstract class AbstractAlertPopulationMap implements AlertPopulationMap {

	protected HashMap<? super PopulationKey, Population> innerMap;

	protected final PopulationType populationType;

	/*
	 * Subclasses should override this class to provide their own concrete implementation. The key
	 * should extract the details of a bug alert which define the population the alert should reside
	 * in. For example, if bug a and b should reside in the same population, a PopulationKey
	 * subclass should be created where key(a).hashCode() == key(b).hashCode() and
	 * key(a).equals(key(b)) returns true.
	 */
	protected static abstract class PopulationKey {
	}

	protected static final class StringPopulationKey extends PopulationKey {
		private final String keyString;

		public StringPopulationKey(String keyString) {
			this.keyString = keyString;
		}

		@Override
		public int hashCode() {
			return keyString.hashCode();
		}

		@Override
		public boolean equals(Object obj) {
			if (obj == null || this.getClass() != obj.getClass())
				return false;
			StringPopulationKey thatKey = (StringPopulationKey) obj;
			return keyString.equals(thatKey.keyString);
		}
	}

	protected abstract PopulationKey getKeyFor(BugInstance alert);

	public AbstractAlertPopulationMap(PopulationType populationType) {
		this.populationType = populationType;
		this.innerMap = new HashMap<PopulationKey, Population>();
	}

	public AlertPopulationMap addAlert(BugInstance bugAlert) {
		Population populationForAlert = getPopulationFor(bugAlert);
		if (populationForAlert == null) {
			populationForAlert = newPopulationFor(bugAlert);
			PopulationKey key = getKeyFor(bugAlert);
			innerMap.put(key, populationForAlert);
		}
		if (populationForAlert.isValidAlert(bugAlert)) {
			populationForAlert.addAlert(bugAlert);
		}

		return this;
	}

	public Population getPopulationFor(BugInstance alert) {
		PopulationKey key = getKeyFor(alert);
		return innerMap.get(key);
	}

	protected abstract Population newPopulationFor(BugInstance alert);

	public final PopulationType type() {
		return this.populationType;
	}

	public Collection<Population> allPopulations() {
		Collection<Population> all = innerMap.values();
		return Collections.unmodifiableCollection(all);
	}
}